package ibatis;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author Rudy 4072883@qq.com
 * @since 2013-7-1
 */
public class GenerateBeansAndXmls {
	private Statement stmt;
	private String packageName;

	public GenerateBeansAndXmls(String url, String packageName) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.jdbc.Driver");
		this.stmt = DriverManager.getConnection(url).createStatement();
		this.packageName = packageName;
	}

	private List<String> getTables() throws SQLException {
		List<String> tables = new ArrayList<String>();
		String sql = "show tables";
		ResultSet rs = stmt.executeQuery(sql);
		while (rs.next()) {
			tables.add(rs.getString(1));
		}
		return tables;
	}

	private Map<String, String> getFields(String table) throws SQLException {
		Map<String, String> fields = new LinkedHashMap<String, String>();
		String sql = "SHOW FULL FIELDS FROM " + table;
		ResultSet rs = stmt.executeQuery(sql);
		while (rs.next()) {
			String key = rs.getString(1);
			String value = rs.getString(2);
			fields.put(key, value);
		}
		return fields;
	}

	private Map<String, String> getComments(String table) throws SQLException {
		Map<String, String> comments = new LinkedHashMap<String, String>();
		String sql = "SHOW FULL FIELDS FROM " + table;
		ResultSet rs = stmt.executeQuery(sql);
		while (rs.next()) {
			String key = rs.getString(1);
			String comment = rs.getString(9);
			comments.put(key, comment);
		}
		return comments;
	}

	private String getPropertyType(String jdbcType) {
		if (jdbcType.toLowerCase().startsWith("varchar")) {
			return "String";
		} else if (jdbcType.toLowerCase().startsWith("bigint")) {
			return "Long";
		} else if (jdbcType.toLowerCase().startsWith("smallint")) {
			return "Integer";
		} else if (jdbcType.toLowerCase().startsWith("tinyint")) {
			return "Byte";
		} else if (jdbcType.toLowerCase().startsWith("datetime")) {
			return "Date";
		} else if (jdbcType.toLowerCase().startsWith("date")) {
			return "java.sql.Date";
		} else if (jdbcType.toLowerCase().startsWith("time")) {
			return "java.sql.Time";
		}
		return "NULL";
	}

	private String getPropertyName(String fieldName) {
		String[] items = fieldName.split("_");
		String propertyName = items[0];
		for (int i = 1; i < items.length; i++) {
			String item = items[i];
			propertyName += item.substring(0, 1).toUpperCase() + item.substring(1);
		}
		return propertyName;
	}

	private String getPropertyGetterName(String fieldName) {
		String[] items = fieldName.split("_");
		String propertyGetterName = "get";
		for (String item : items) {
			propertyGetterName += item.substring(0, 1).toUpperCase() + item.substring(1);
		}
		return propertyGetterName;
	}

	private String getPropertySetterName(String fieldName) {
		String[] items = fieldName.split("_");
		String propertyGetterName = "set";
		for (String item : items) {
			propertyGetterName += item.substring(0, 1).toUpperCase() + item.substring(1);
		}
		return propertyGetterName;
	}

	private String getBeanName(String table) {
		String[] items = table.split("_");
		String beanName = "";
		for (String item : items) {
			beanName += item.substring(0, 1).toUpperCase() + item.substring(1);
		}
		return beanName;
	}

	private String getBeanString(String table) throws SQLException {
		Map<String, String> fields = this.getFields(table);
		Map<String, String> comments = this.getComments(table);
		StringBuffer bean = new StringBuffer();
		bean.append("package " + this.packageName + ";");

		bean.append("import java.util.Date;");
		bean.append("import java.util.Map;");
		bean.append("import com.gleasy.beans.BaseObject;");

		bean.append("public class " + this.getBeanName(table) + " extends BaseObject {");
		Set<String> fieldNames = fields.keySet();

		for (String fieldName : fieldNames) {
			String fieldType = fields.get(fieldName);
			bean.append("/**" + comments.get(fieldName) + "*/");
			bean.append("private " + this.getPropertyType(fieldType) + " " + this.getPropertyName(fieldName) + ";");
		}

		bean.append("public " + this.getBeanName(table) + "() {}");

		for (String fieldName : fieldNames) {
			String fieldType = fields.get(fieldName);
			bean.append("public " + this.getPropertyType(fieldType) + " " + this.getPropertyGetterName(fieldName) + "() {");
			bean.append("return " + this.getPropertyName(fieldName) + ";");
			bean.append("}");

			bean.append("public void " + this.getPropertySetterName(fieldName) + "(" + this.getPropertyType(fieldType) + " " + this.getPropertyName(fieldName) + ") {");
			bean.append("this." + this.getPropertyName(fieldName) + " = " + this.getPropertyName(fieldName) + ";");
			bean.append("}");
		}
		bean.append("}");
		return bean.toString();
	}

	private String getBeanInstanceName(String table) {
		return this.getBeanName(table).substring(0, 1).toLowerCase() + this.getBeanName(table).substring(1);
	}

	public String getBeanConfigString(String table) throws SQLException {
		Map<String, String> fields = this.getFields(table);
		StringBuffer bean = new StringBuffer();
		bean.append("<?xml version='1.0' encoding='UTF-8'?>\r\n");
		bean.append("<!DOCTYPE sqlMap PUBLIC '-//ibatis.apache.org//DTD SQL Map 2.0//EN' 'http://ibatis.apache.org/dtd/sql-map-2.dtd'>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<sqlMap namespace='" + this.getBeanInstanceName(table) + "'>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<typeAlias alias='" + this.getBeanName(table) + "' type='" + this.packageName + "." + this.getBeanName(table) + "' />");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<resultMap id='" + this.getBeanInstanceName(table) + "Map' class='" + this.getBeanName(table) + "'>");
		Set<String> fieldNames = fields.keySet();

		for (String fieldName : fieldNames) {
			String fieldType = fields.get(fieldName);
			if ("DATETIME".equals(fieldType.toUpperCase())) {
				fieldType = "TIMESTAMP";
			}
			bean.append("<result column='" + fieldName + "' property='" + this.getPropertyName(fieldName) + "' jdbcType='" + fieldType.toUpperCase().replaceAll("[(].*[)]", "")
					+ "' />");
		}

		bean.append("</resultMap>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<insert id='save' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("insert into " + table + "(");

		for (String fieldName : fieldNames) {
			bean.append(fieldName + ",");
		}
		bean.setCharAt(bean.length() - 1, ')');
		bean.append("values(");

		for (String fieldName : fieldNames) {
			bean.append("#" + this.getPropertyName(fieldName) + "#,");
		}
		bean.setCharAt(bean.length() - 1, ')');

		bean.append("</insert>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<select id='get' resultMap='" + this.getBeanInstanceName(table) + "Map' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("select * from " + table + " where id = #id#");
		bean.append("</select>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<delete id='delete' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("delete from " + table + " where id = #id#");
		bean.append("</delete>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<delete id='deleteSelective' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("delete from " + table);
		bean.append("<dynamic prepend='where'>");
		for (String fieldName : fieldNames) {
			bean.append("<isNotNull prepend='and ' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isNotNull>");
		}
		bean.append("</dynamic>");
		bean.append("</delete>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<delete id='deletePropertyAvailable' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("delete from " + table);
		bean.append("<dynamic prepend='where'>");
		for (String fieldName : fieldNames) {
			bean.append("<isPropertyAvailable prepend='and ' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isPropertyAvailable>");
		}
		bean.append("</dynamic>");
		bean.append("</delete>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<update id='update' parameterClass='" + this.getBeanName(table) + "' >");
		bean.append("update " + table + " set ");
		for (String fieldName : fieldNames) {
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#,");
		}
		bean.setCharAt(bean.length() - 1, ' ');
		bean.append("where id = #id#");
		bean.append("</update>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<update id='updateSelective' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("update " + table);
		bean.append("<dynamic prepend='set'>");
		for (String fieldName : fieldNames) {
			bean.append("<isNotNull prepend=',' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isNotNull>");
		}
		bean.append("</dynamic>");
		bean.append("where id = #id#");
		bean.append("</update>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<update id='updatePropertyAvailable' parameterClass='java.util.Map'>");
		bean.append("update " + table);
		bean.append("<dynamic prepend='set'>");
		for (String fieldName : fieldNames) {
			bean.append("<isPropertyAvailable prepend=',' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isPropertyAvailable>");
		}
		bean.append("</dynamic>");
		bean.append("where id = #id#");
		bean.append("</update>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<select id='search' resultMap='" + this.getBeanInstanceName(table) + "Map' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("select * from " + table);
		bean.append("<dynamic prepend='where'>");
		for (String fieldName : fieldNames) {
			bean.append("<isNotNull prepend='and ' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isNotNull>");
		}
		bean.append("</dynamic>");
		bean.append("</select>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<select id='searchPropertyAvailable' resultMap='" + this.getBeanInstanceName(table) + "Map' parameterClass='java.util.Map'>");
		bean.append("select * from " + table);
		bean.append("<dynamic prepend='where'>");
		for (String fieldName : fieldNames) {
			bean.append("<isPropertyAvailable prepend='and ' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isPropertyAvailable>");
		}
		bean.append("</dynamic>");
		bean.append("</select>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<select id='searchPage' resultMap='" + this.getBeanInstanceName(table) + "Map' parameterClass='java.util.Map'>");
		bean.append("select * from " + table);
		bean.append("<dynamic prepend='where'>");
		for (String fieldName : fieldNames) {
			bean.append("<isNotNull prepend='and ' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isNotNull>");
		}
		bean.append("</dynamic>");
		bean.append("limit #offset#,#pageSize#");
		bean.append("</select>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<select id='searchPropertyAvailablePage' resultMap='" + this.getBeanInstanceName(table) + "Map' parameterClass='java.util.Map'>");
		bean.append("select * from " + table);
		bean.append("<dynamic prepend='where'>");
		for (String fieldName : fieldNames) {
			bean.append("<isPropertyAvailable prepend='and ' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isPropertyAvailable>");
		}
		bean.append("</dynamic>");
		bean.append("limit #offset#,#pageSize#");
		bean.append("</select>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<select id='countSelective' resultClass='Integer' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("select count(*) from " + table);
		bean.append("<dynamic prepend='where'>");
		for (String fieldName : fieldNames) {
			bean.append("<isNotNull prepend='and ' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isNotNull>");
		}
		bean.append("</dynamic>");
		bean.append("</select>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<select id='countPropertyAvailable' resultClass='Integer' parameterClass='java.util.Map'>");
		bean.append("select count(*) from " + table);
		bean.append("<dynamic prepend='where'>");
		for (String fieldName : fieldNames) {
			bean.append("<isPropertyAvailable prepend='and ' property='" + this.getPropertyName(fieldName) + "'>");
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#");
			bean.append("</isPropertyAvailable>");
		}
		bean.append("</dynamic>");
		bean.append("</select>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("</sqlMap>");

		return bean.toString();
	}
	
	public String getBeanConfigStringMin(String table) throws SQLException {
		Map<String, String> fields = this.getFields(table);
		StringBuffer bean = new StringBuffer();
		bean.append("<?xml version='1.0' encoding='UTF-8'?>\r\n");
		bean.append("<!DOCTYPE sqlMap PUBLIC '-//ibatis.apache.org//DTD SQL Map 2.0//EN' 'http://ibatis.apache.org/dtd/sql-map-2.dtd'>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<sqlMap namespace='" + this.getBeanInstanceName(table) + "'>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<typeAlias alias='" + this.getBeanName(table) + "' type='" + this.packageName + "." + this.getBeanName(table) + "' />");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<resultMap id='" + this.getBeanInstanceName(table) + "Map' class='" + this.getBeanName(table) + "'>");
		Set<String> fieldNames = fields.keySet();

		for (String fieldName : fieldNames) {
			String fieldType = fields.get(fieldName);
			if ("DATETIME".equals(fieldType.toUpperCase())) {
				fieldType = "TIMESTAMP";
			}
			bean.append("<result column='" + fieldName + "' property='" + this.getPropertyName(fieldName) + "' jdbcType='" + fieldType.toUpperCase().replaceAll("[(].*[)]", "")
					+ "' />");
		}

		bean.append("</resultMap>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<insert id='save' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("insert into " + table + "(");

		for (String fieldName : fieldNames) {
			bean.append(fieldName + ",");
		}
		bean.setCharAt(bean.length() - 1, ')');
		bean.append("values(");

		for (String fieldName : fieldNames) {
			bean.append("#" + this.getPropertyName(fieldName) + "#,");
		}
		bean.setCharAt(bean.length() - 1, ')');

		bean.append("</insert>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<select id='get' resultMap='" + this.getBeanInstanceName(table) + "Map' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("select * from " + table + " where id = #id#");
		bean.append("</select>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<delete id='delete' parameterClass='" + this.getBeanName(table) + "'>");
		bean.append("delete from " + table + " where id = #id#");
		bean.append("</delete>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("<update id='update' parameterClass='" + this.getBeanName(table) + "' >");
		bean.append("update " + table + " set ");
		for (String fieldName : fieldNames) {
			bean.append(fieldName + " = #" + this.getPropertyName(fieldName) + "#,");
		}
		bean.setCharAt(bean.length() - 1, ' ');
		bean.append("where id = #id#");
		bean.append("</update>");

		bean.append("\r\n");
		bean.append("\r\n");

		bean.append("</sqlMap>");

		return bean.toString();
	}

	private void generateBean(String table) throws IOException, SQLException {
		File folder = new File("src/" + packageName.replace(".", "/"));
		folder.mkdirs();
		FileWriter fw = new FileWriter("src/" + packageName.replace(".", "/") + "/" + this.getBeanName(table) + ".java");
		fw.write(this.getBeanString(table));
		fw.close();
	}

	private void generateBeanConfig(String table) throws IOException, SQLException {
		File folder = new File("src/" + packageName.replace(".", "/"));
		folder.mkdirs();
		FileWriter fw = new FileWriter("src/" + packageName.replace(".", "/") + "/" + this.getBeanName(table) + ".map.xml");
		fw.write(this.getBeanConfigStringMin(table));
		fw.close();
	}

	public void generate() throws SQLException, IOException {
		List<String> tables = this.getTables();
		for (String table : tables) {
			this.generateBean(table);
			this.generateBeanConfig(table);
		}
	}

	public static void main(String[] args) throws ClassNotFoundException, SQLException, IOException {
		String url = "jdbc:mysql://192.168.0.11:3307/attendance-local-0?user=mysql3307&password=mysql3307";
		String packageName = "com.gleasy.attendance.domain";
		GenerateBeansAndXmls g = new GenerateBeansAndXmls(url, packageName);
		g.generate();
	}
}